home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue28 / amclock / AMCLOCK.ZIP / AMClock.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1997-10-13  |  17.5 KB  |  656 lines

  1. { TAMClock version 1.0 for Delphi 2.x and 3.x
  2.   Copyright ⌐ October 1997 by Alexander Meeder
  3.  
  4.   This analog clock is highly configurable. You can use a picture as the face of
  5.   the clock (*.bmp, *.ico, *.emf and *.wmf). You can also configure the
  6.   hour-, minute- and secondhand (backradius, radius, width, color). Additional
  7.   you can set the interval (of the thread, no timer is used with this clock), the
  8.   Priority and the center of the clock. This clock also has several events to
  9.   trigger hour, minute and second events and a global timer event (every interval).
  10.  
  11.   TCenter properties (see also Center and AutoCenter properties of TAMClock)
  12.     X  : default 50 except when Autocenter
  13.     Y  : default 50    "     "      "
  14.  
  15.   THand properties (see also Hours-, Minutes- and SecondsHand properties of TAMClock)
  16.     BackRadius : backradius of the hour, minute or second hand
  17.     Color      : color      of  "    "      "    "    "     "
  18.     Radius     : radius     of  "    "      "    "    "     "
  19.     Width      : width      of  "    "      "    "    "     "
  20.  
  21.   TAMClock properties
  22.     AutoCenter       : default True, centers clock on canvas
  23.     Center           : TCenter-object with X,Y value
  24.     HoursHand        : THand-object see description above
  25.     Interval         : Update interval (and interval of OnTimer-event)
  26.     Interactive      : default False, when true you can click at runtime on clock
  27.                        to move the center
  28.     MinutesHand      : THand-object see description above
  29.     Picture          : default SAMPLECLOCK.BMP, every *.bmp, *.emf, *.ico or
  30.                        *.wmf-file
  31.     Priority         : default tpNormal, Thread priority (tpNormal, tpHight etc.)
  32.     SecondsHand      : THand-object see description above
  33.     Transparent      : default True
  34.     TransparentColor : default clOlive, the picture-color to replace with the color
  35.                        property of this component (default the form, panel etc.
  36.                        color)
  37.  
  38.   TAMClocl events
  39.     OnHour        : every hour
  40.     OnMinute      : every minute
  41.     OnSecond      : every second
  42.     OnTimer       : triggered every X milisecond (X = interval property),
  43.                     just like in Delphi's TTimer-object
  44.  
  45.     OnMouseEnter  : when the mouse enters the control
  46.     OnMouseLeave  : when the mouse leaves the control
  47. }
  48.  
  49. unit AMClock;
  50.  
  51. interface
  52.  
  53. uses
  54.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  55.   ExtCtrls;
  56.  
  57. type
  58.   TCmMouseEnter = record
  59.     Msg    : Cardinal;
  60.     Unused : Integer;
  61.     Sender : TControl;             
  62.     Result : LongInt;
  63.   end;
  64.   TCmMouseLeave = TCmMouseEnter;
  65.  
  66.   TMouseEnterLeave = procedure (Sender: TObject) of object;
  67.  
  68.   THour   = procedure (Sender: TObject; Hour: word) of object;
  69.   TMinute = procedure (Sender: TObject; Minute: word) of object;
  70.   TSecond = procedure (Sender: TObject; Second: word) of object;
  71.  
  72.   TAMClock = class;
  73.   TThrdTimer = class;
  74.  
  75.   TTimerThread = class(TThread)
  76.     OwnerTimer: TThrdTimer;
  77.     procedure Execute; override;
  78.   end;
  79.  
  80.   TThrdTimer = class(TComponent)
  81.   private
  82.     FEnabled: boolean;
  83.     FInterval: word;
  84.     FOnTimer: TNotifyEvent;
  85.     FTimerThread: TTimerThread;
  86.     FThreadPriority: TThreadPriority;
  87.  
  88.     procedure SetEnabled(Value: Boolean);
  89.     procedure SetInterval(Value: word);
  90.     procedure SetThreadPriority(Value: TThreadPriority);
  91.     procedure Timer; dynamic;
  92.   protected
  93.     procedure UpdateTimer;
  94.   public
  95.     constructor Create(AOwner: TComponent); override;
  96.     destructor Destroy; override;
  97.     property TimerThread: TTimerThread read FTimerThread write FTimerThread;
  98.   published
  99.     property Enabled: boolean read FEnabled write SetEnabled default True;
  100.     property Interval: word read FInterval write SetInterval default 250;
  101.     property Priority: TThreadPriority read FThreadPriority write SetThreadPriority default tpNormal;
  102.     property OnTimer: TNotifyEvent read FOnTimer write FOnTimer;
  103.   end;
  104.  
  105.   TCenter = class(TPersistent)
  106.   private
  107.     FX: integer;
  108.     FY: integer;
  109.     FParent: TAMClock;
  110.  
  111.     procedure SetCenter(Index, Value: integer);
  112.   protected
  113.     procedure UpdateParent;
  114.   public
  115.     constructor Create;
  116.     property Parent: TAMClock read FParent write FParent;
  117.   published
  118.     property X: integer index 0 read FX write SetCenter default 50;
  119.     property Y: integer index 1 read FY write SetCenter default 50;
  120.   end;
  121.  
  122.   THand = class(TPersistent)
  123.   private
  124.     FBackRadius: integer;
  125.     FColor: TColor;
  126.     FParent: TAMClock;
  127.     FRadius: integer;
  128.     FWidth: integer;
  129.  
  130.     procedure SetBackRadius(Value: integer);
  131.     procedure SetColor(Value: TColor);
  132.     procedure SetRadius(Value: integer);
  133.     procedure SetWidth(Value: integer);
  134.   protected
  135.     procedure UpdateParent;  
  136.   public
  137.     constructor Create;
  138.     property Parent: TAMClock read FParent write FParent;
  139.   published
  140.     property BackRadius: integer read FBackRadius write SetBackRadius default 10;
  141.     property Color: TColor read FColor write SetColor default clRed;
  142.     property Radius: integer read FRadius write SetRadius default 90;
  143.     property Width: integer read FWidth write SetWidth default 2;
  144.   end;
  145.  
  146.   TAMClock = class(TCustomControl)
  147.   private
  148.     FAutoCenter: boolean;
  149.     FCenter: TCenter;
  150.     FHoursHand: THand;
  151.     FInteractive: boolean;
  152.     FInterval: word;
  153.     FMinutesHand: THand;
  154.     FSecondsHand: THand;
  155.     FPicture: TPicture;
  156.     FPriority: TThreadPriority;
  157.     FTransparent: boolean;
  158.     FTransparentColor: TColor;
  159.  
  160.     FHour: THour;
  161.     FMinute: TMinute;
  162.     FSecond: TSecond;
  163.     FOnTimer: TNotifyEvent;
  164.     FMouseEnter : TMouseEnterLeave;
  165.     FMouseLeave : TMouseEnterLeave;
  166.  
  167.     Timer: TThrdTimer;
  168.     Buffer: TBitmap;
  169.     h,m,s: word;
  170.     OldHour, OldMinute, OldSecond: word;
  171.  
  172.     procedure SetAutoCenter(Value: boolean);
  173.     procedure SetInterval(Value: word);
  174.     procedure SetPicture(Value: TPicture);
  175.     function  GetPriority: TThreadPriority;
  176.     procedure SetPriority(Value: TThreadPriority);
  177.     procedure SetTransparent(Value: boolean);
  178.     procedure SetTransparentColor(Value: TColor);
  179.   protected
  180.     procedure UpdateClock(Sender: TObject);
  181.     procedure DrawHand(XCenter, YCenter, Radius, BackRadius, HandWidth: integer; HandColor: TColor; Angle: Real);
  182.     procedure CmEnabledChanged(var Message: TWmNoParams); message CM_ENABLEDCHANGED;
  183.     procedure CmMouseEnter(var Message: TCmMouseEnter); message CM_MOUSEENTER;
  184.     procedure CmMouseLeave(var Message: TCmMouseLeave); message CM_MOUSELEAVE;
  185.     procedure CmVisibleChanged(var Message: TWmNoParams); message CM_VISIBLECHANGED;
  186.     procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
  187.     procedure Loaded; override;
  188.     procedure Paint; override;
  189.   public
  190.     constructor Create(AOwner: TComponent); override;
  191.     destructor Destroy; override;
  192.     property Canvas;
  193.   published
  194.     property AutoCenter: boolean read FAutoCenter write SetAutoCenter default True;
  195.     property Center: TCenter read FCenter write FCenter;
  196.     property Color;
  197.     property DragCursor;
  198.     property DragMode;
  199.     property Enabled;
  200.     property Hint;
  201.     property HoursHand: THand read FHoursHand write FHoursHand;
  202.     property Interval: word read FInterval write SetInterval default 250;
  203.     property Interactive: boolean read FInteractive write FInteractive default False;
  204.     property MinutesHand: THand read FMinutesHand write FMinutesHand;
  205.     property ParentColor;
  206.     property ParentShowHint;
  207.     property Picture: TPicture read FPicture write SetPicture;
  208.     property PopupMenu;
  209.     property Priority: TThreadPriority read GetPriority write SetPriority default tpNormal;
  210.     property SecondsHand: THand read FSecondsHand write FSecondsHand;
  211.     property ShowHint;
  212.     property Transparent: boolean read FTransparent write SetTransparent default True;
  213.     property TransparentColor: TColor read FTransparentColor write SetTransparentColor default clOlive;
  214.     property Visible;
  215.     property OnClick;
  216.     property OnDragDrop;
  217.     property OnDragOver;
  218.     property OnEndDrag;
  219.     property OnEnter;
  220.     property OnExit;
  221.     property OnHour: THour read FHour write FHour;
  222.     property OnKeyDown;
  223.     property OnKeyPress;
  224.     property OnKeyUp;
  225.     property OnMinute: TMinute read FMinute write FMinute;
  226.     property OnMouseDown;
  227.     property OnMouseEnter : TMouseEnterLeave read FMouseEnter write FMouseEnter;
  228.     property OnMouseLeave : TMouseEnterLeave read FMouseLeave write FMouseLeave;
  229.     property OnMouseMove;
  230.     property OnMouseUp;
  231.     property OnSecond: TSecond read FSecond write FSecond;
  232.     property OnTimer: TNotifyEvent read FOnTimer write FOnTimer;
  233.     property OnStartDrag;
  234.   end;
  235.  
  236. procedure Register;
  237.  
  238. implementation
  239.  
  240. {$R AMClock.res}
  241.  
  242. procedure TAMClock.UpdateClock(Sender: TObject);
  243. var
  244.   hsec: word;
  245. begin
  246.   DecodeTime(Time,h,m,s,hsec);
  247.   Repaint;
  248.   if s <> OldSecond then
  249.   begin
  250.     if Assigned(FSecond) then FSecond(Self, s);
  251.     OldSecond := s;
  252.   end;
  253.   if m <> OldMinute then
  254.   begin
  255.     if Assigned(FMinute) then FMinute(Self, m);
  256.     OldMinute := m;
  257.   end;
  258.   if h <> OldHour then
  259.   begin
  260.     if Assigned(FHour) then FHour(Self, h);
  261.     OldHour := h;
  262.   end;
  263.   if Assigned(FOnTimer) then FOnTimer(Self);
  264. end;
  265.  
  266. procedure TAMClock.Paint;
  267. var
  268.   Angle: real;
  269. begin
  270.   Buffer.Width := Self.Width;
  271.   Buffer.Height := Self.Height;
  272.   with Buffer.Canvas do
  273.   begin
  274.     Brush.Color := Self.Color;
  275.     Brush.Style := bsSolid;
  276.     Pen.Color := Self.Color;
  277.     Rectangle(0,0,Width,Height);
  278.     if FTransparent then
  279.        BrushCopy(ClientRect, FPicture.Bitmap, ClientRect, FTransparentColor)
  280.     else
  281.        Draw(0,0,FPicture.Graphic);
  282.   end;
  283.  
  284.   Angle := 2 * PI * (h + m / 60) / 12;
  285.   DrawHand(100,100,HoursHand.Radius,HoursHand.BackRadius,HoursHand.Width,HoursHand.Color,Angle);
  286.   Angle := 2 * PI * m / 60;
  287.   DrawHand(100,100,MinutesHand.Radius,MinutesHand.BackRadius,MinutesHand.Width,MinutesHand.Color,Angle);
  288.   Angle := 2 * PI * s / 60;
  289.   DrawHand(100,100,SecondsHand.Radius,SecondsHand.BackRadius,SecondsHand.Width,SecondsHand.Color,Angle);
  290.  
  291.   Canvas.Draw(0,0,Buffer);
  292. end;
  293.  
  294. procedure TAMClock.Loaded;
  295. var
  296.   HSec: word;
  297. begin
  298.   inherited Loaded;
  299.   Buffer.Width := Self.ClientWidth;
  300.   Buffer.Height := Self.ClientHeight;
  301.   Buffer.Canvas.Brush.Color := Color;
  302.   if AutoCenter then
  303.   begin
  304.     with Center do
  305.     begin
  306.       X := Width div 2;
  307.       Y := Height div 2;
  308.     end;
  309.   end;
  310.   DecodeTime(Now, OldHour, OldMinute, OldSecond, HSec);
  311. end;
  312.  
  313. procedure TAMClock.SetAutoCenter(Value: boolean);
  314. begin
  315.   if Value <> FAutoCenter then
  316.   begin
  317.     if Value then
  318.     begin
  319.       with FCenter do
  320.       begin
  321.         X := Width div 2;
  322.         Y := Height div 2;
  323.       end;
  324.     end;
  325.     FAutoCenter := Value;
  326.     Repaint;
  327.   end;
  328. end;
  329.  
  330. procedure TAMClock.SetInterval(Value: word);
  331. begin
  332.   if Value <> FInterval then
  333.   begin
  334.     FInterval := Value;
  335.     Timer.Interval := FInterval;
  336.     Repaint;
  337.   end;
  338. end;
  339.  
  340. procedure TAMClock.SetPicture(Value: TPicture);
  341. begin
  342.   if Transparent and not (Value.Graphic is TBitmap) then Transparent := False;
  343.   FPicture.Assign(value);
  344.   Width := FPicture.Width;
  345.   Buffer.Width := Width;
  346.   Height := FPicture.Height;
  347.   Buffer.Height := Height;
  348.   if AutoCenter then
  349.   begin
  350.     with Center do
  351.     begin
  352.       X := Width div 2;
  353.       Y := Height div 2;
  354.     end;
  355.   end;
  356.   Repaint;
  357. end;
  358.  
  359. function TAMClock.GetPriority: TThreadPriority;
  360. begin
  361.   Result := Timer.Priority;
  362. end;
  363.  
  364. procedure TAMClock.SetPriority(Value: TThreadPriority);
  365. begin
  366.   if Value <> FPriority then
  367.   begin
  368.     FPriority := Value;
  369.     Timer.Priority := FPriority;
  370.   end;
  371. end;
  372.  
  373. procedure TAMClock.SetTransparent(Value: boolean);
  374. begin
  375.   if FPicture.Graphic is TBitmap then
  376.     if Value <> FTransparent then
  377.     begin
  378.       FTransparent := Value;
  379.       FTransparentColor := FPicture.Bitmap.Canvas.Pixels[0,FPicture.Bitmap.Height-1];
  380.       Repaint;
  381.     end
  382.   else
  383.     FTransparent := False;
  384. end;
  385.  
  386. procedure TAMClock.SetTransparentColor(Value: TColor);
  387. begin
  388.   if Value <> FTransparentColor then
  389.   begin
  390.     FTransparentColor := Value;
  391.     if (Value <> clNone) and not Transparent then FTransparent := True;
  392.     Repaint;
  393.   end;
  394. end;
  395.  
  396. procedure TAMClock.DrawHand(XCenter, YCenter, Radius, BackRadius, HandWidth: integer; HandColor: TColor; Angle: Real);
  397. var
  398.   X,Y: integer;
  399. begin
  400.   with Buffer.Canvas.Pen do
  401.   begin
  402.     Width := HandWidth;
  403.     Color := HandColor;
  404.   end;
  405.   with Buffer.Canvas do
  406.   begin
  407.     Angle := (Angle + 3 * PI / 2);
  408.     X := Center.X - Round(BackRadius * cos(Angle));
  409.     Y := Center.Y - Round(BackRadius * sin(Angle));
  410.     MoveTo(Center.X,Center.Y);
  411.     LineTo(X,Y);
  412.     X := Center.X + Round(Radius * cos(Angle));
  413.     Y := Center.Y + Round(Radius * sin(Angle));
  414.     MoveTo(Center.X,Center.Y);
  415.     LineTo(X,Y);
  416.   end;
  417. end;
  418.  
  419. procedure TAMClock.CmEnabledChanged(var Message: TWmNoParams);
  420. begin
  421.   inherited;
  422.   Timer.Enabled := Self.Enabled;
  423.   Repaint;
  424. end;
  425.  
  426. procedure TAMClock.CmMouseEnter(var Message: TCmMouseEnter);
  427. begin
  428.   inherited;
  429.   if Assigned(FMouseEnter) then FMouseEnter(Self);
  430. end;
  431.  
  432. procedure TAMClock.CmMouseLeave(var Message: TCmMouseLeave);
  433. begin
  434.   inherited;
  435.   if Assigned(FMouseLeave) then FMouseLeave(Self);
  436. end;
  437.  
  438. procedure TAMClock.CmVisibleChanged(var Message: TWmNoParams);
  439. begin
  440.   inherited;
  441.   Repaint;
  442. end;
  443.  
  444. procedure TAMClock.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  445. begin
  446.   if (Button = mbLeft) and Interactive then
  447.   begin
  448.     Center.X := X;
  449.     Center.Y := Y;
  450.   end;
  451.   inherited MouseDown(Button, Shift, X, Y);
  452. end;
  453.  
  454. constructor TAMClock.Create(AOwner: TComponent);
  455. begin
  456.   inherited Create(AOwner);
  457.   ControlStyle := ControlStyle + [csOpaque];
  458.   SetBounds(0,0,100,100);
  459.   FInterval := 250;
  460.   Timer := TThrdTimer.Create(self);
  461.   Timer.Interval := FInterval;
  462.   Timer.OnTimer := UpdateClock;
  463.   Buffer := TBitmap.Create;
  464.   FPicture := TPicture.Create;
  465.   FPicture.Bitmap.Handle := LoadBitmap(hInstance, 'SAMPLECLOCK');
  466.   FPriority := tpNormal;
  467.   FSecondsHand := THand.Create;
  468.   with FSecondsHand do
  469.   begin
  470.     Parent := Self;
  471.     BackRadius := 10;
  472.     Color := clRed;
  473.     Radius := 48;
  474.     Width := 1;
  475.   end;
  476.   FMinutesHand := THand.Create;
  477.   with FMinutesHand do
  478.   begin
  479.     Parent := Self;
  480.     BackRadius := 0;
  481.     Color := clBlack;
  482.     Radius := FSecondsHand.Radius * 90 div 100;
  483.     Width := 2;
  484.   end;
  485.   FHoursHand := THand.Create;
  486.   with FHoursHand do
  487.   begin
  488.     Parent := Self;
  489.     BackRadius := 0;
  490.     Color := clBlack;
  491.     Radius := FSecondsHand.Radius * 70 div 100;
  492.     Width := 2;
  493.   end;
  494.   FCenter := TCenter.Create;
  495.   FCenter.Parent := Self;
  496.   FAutoCenter := True;
  497.   FInteractive := False;
  498.   FTransparent := True;
  499.   FTransparentColor := clOlive;
  500. end;
  501.  
  502. destructor TAMClock.Destroy;
  503. begin
  504.   FPicture.Free;
  505.   Buffer.Free;
  506.   Timer.Free;
  507.   inherited Destroy;
  508. end;
  509.  
  510. // Begin of TThrdTimer-implementation...
  511. procedure TTimerThread.Execute;
  512. begin
  513.   Priority := OwnerTimer.Priority;
  514.   repeat
  515.     SleepEx(OwnerTimer.Interval, False);
  516.     Synchronize(OwnerTimer.Timer);
  517.   until Terminated;
  518. end;
  519.  
  520. procedure TThrdTimer.UpdateTimer;
  521. begin
  522.   if not TimerThread.Suspended then TimerThread.Suspend;
  523.   if (FInterval <> 0) and FEnabled then
  524.      if TimerThread.Suspended then TimerThread.Resume;
  525. end;
  526.  
  527. procedure TThrdTimer.SetEnabled(Value: boolean);
  528. begin
  529.   if Value <> FEnabled then
  530.   begin
  531.     FEnabled := Value;
  532.     UpdateTimer;
  533.   end;
  534. end;
  535.  
  536. procedure TThrdTimer.SetInterval(Value: Word);
  537. begin
  538.   if Value <> FInterval then
  539.   begin
  540.     FInterval := Value;
  541.     UpdateTimer;
  542.   end;
  543. end;
  544.  
  545. procedure TThrdTimer.SetThreadPriority(Value: TThreadPriority);
  546. begin
  547.   if Value <> FThreadPriority then
  548.   begin
  549.     FThreadPriority := Value;
  550.     UpdateTimer;
  551.   end;
  552. end;
  553.  
  554. procedure TThrdTimer.Timer;
  555. begin
  556.   if Assigned(FOntimer) then FOnTimer(Self);
  557. end;
  558.  
  559. constructor TThrdTimer.Create(AOwner: TComponent);
  560. begin
  561.   inherited Create(AOwner);
  562.   FEnabled := True;
  563.   FInterval := 250;
  564.   FThreadPriority := tpNormal;
  565.   FTimerThread := TTimerThread.Create(False);
  566.   FTimerThread.OwnerTimer := Self;
  567. end;
  568.  
  569. destructor TThrdTimer.Destroy;
  570. begin
  571.   FEnabled := False;
  572.   UpdateTimer;
  573.   FTimerThread.Free;
  574.   inherited Destroy;
  575. end;
  576.  
  577. // Begin of THand-implementation...
  578. procedure THand.SetBackRadius(Value: integer);
  579. begin
  580.   if Value <> FBackRadius then
  581.   begin
  582.     FBackRadius := Value;
  583.     UpdateParent;
  584.   end;
  585. end;
  586.  
  587. procedure THand.SetColor(Value: TColor);
  588. begin
  589.   if Value <> FColor then
  590.   begin
  591.     FColor := Value;
  592.     UpdateParent;
  593.   end;
  594. end;
  595.  
  596. procedure THand.SetRadius(Value: integer);
  597. begin
  598.   if Value <> FRadius then
  599.   begin
  600.     FRadius := Value;
  601.     UpdateParent;
  602.   end;
  603. end;
  604.  
  605. procedure THand.SetWidth(Value: integer);
  606. begin
  607.   if Value <> FWidth then
  608.   begin
  609.     FWidth := Value;
  610.     UpdateParent;
  611.   end;
  612. end;
  613.  
  614. procedure THand.UpdateParent;
  615. begin
  616.   Parent.Repaint;
  617. end;
  618.  
  619. constructor THand.Create;
  620. begin
  621.   inherited Create;
  622.   FBackRadius := 10;
  623.   FColor := clRed;
  624.   FRadius := 90;
  625.   FWidth := 2;
  626. end;
  627.  
  628. procedure TCenter.SetCenter(Index, Value: integer);
  629. begin
  630.   case Index of
  631.     0: if Value <> FX then FX := Value;
  632.     1: if Value <> FY then FY := Value;
  633.   end;
  634.   if Parent.AutoCenter then Parent.AutoCenter := False;
  635.   UpdateParent;
  636. end;
  637.  
  638. procedure TCenter.UpdateParent;
  639. begin
  640.   Parent.Repaint;
  641. end;
  642.  
  643. constructor TCenter.Create;
  644. begin
  645.   inherited Create;
  646.   FX := 50;
  647.   FY := 50;
  648. end;
  649.  
  650. procedure Register;
  651. begin
  652.   RegisterComponents('Samples', [TAMClock]);
  653. end;
  654.  
  655. end.
  656.